﻿using Fuse8.ContentManagementFramework.Domain;
using Fuse8.ContentManagementFramework.Domain.Bases;
using Fuse8.ContentManagementFramework.Domain.Commands;
using Fuse8.ContentManagementFramework.Domain.Commands.CommandParameters;
using Fuse8.DomainFramework.BusinessService;
using Fuse8.DomainFramework.Common;
using Fuse8.DomainFramework.Common.Locator;
using Fuse8.DomainFramework.ValidationService;
using System.Collections.Generic;
using System.Linq;
using Fuse8.ContentManagementFramework.DataAccess.SqlAccess.Commands;

namespace Fuse8.ContentManagementFramework.BizFacade.Services
{
    /// <summary>
    /// Page Business Service
    /// </summary>
    public class CmsPageService : CmsEntityRevisionService<CmsPage>
    {
        /// <summary>
        /// Gets page with level 1.
        /// </summary>
        public virtual CmsPage RootPage
        {
            get
            {
                var data = InnerFindAll(1);

                return data.Any() ?
                    data.FirstOrDefault() :
                    null;
            }
        }

        /// <summary>
        /// Finds page by unique id
        /// </summary>
        /// <param name="id">Unique id of page to find</param>
        /// <returns>An object of <see cref="CmsPage" /> if page exists or null if does not</returns>
        public override CmsPage FindOne(int id)
        {
            var command = DependencyServiceLocator.Current.GetInstance<CmsPageSelectOneCommand>();

            var commandParameter = new CmsPageSelectAllCommandParameter
            {
                Id = id
            };

            SiteAwareEntityObjectMediator.SetSiteEntity(commandParameter);

            AdministrationAreaMediator.SetAreaFlag(commandParameter);

            command.ConfigureCommand(commandParameter);

            return FindOne(command);
        }

        /// <summary>
        /// Finds page by public id
        /// </summary>
        /// <param name="publicPageId">Public Page Id of page to find</param>
        /// <returns>An object of <see cref="CmsPage" /> if page exists or null if does not</returns>
        /// <remarks>Can return different pages in admin area and front end. It depends on workflow.</remarks>
        public virtual CmsPage FindByPublicId(int publicPageId)
        {
            var command = DependencyServiceLocator.Current.GetInstance<CmsPageSelectOneCommand>();

            var commandParameter = new CmsPageSelectAllCommandParameter
            {
                Id = publicPageId,
                PublicPageId = publicPageId
            };

            SiteAwareEntityObjectMediator.SetSiteEntity(commandParameter);

            AdministrationAreaMediator.SetAreaFlag(commandParameter);

            command.ConfigureCommand(commandParameter);

            return FindOne(command);
        }

        /// <summary>
        /// Finds page by public id
        /// </summary>
        /// <param name="pageKey">Page key of page to find</param>
        /// <returns>An object of <see cref="CmsPage" /> if page exists or null if does not</returns>
        /// <remarks>Can return different pages in admin area and front end. It depends on workflow.</remarks>
        public virtual CmsPage FindByPageKey(string pageKey)
        {
            var command = DependencyServiceLocator.Current.GetInstance<CmsPageSelectOneCommand>();

            var commandParameter = new CmsPageSelectAllCommandParameter
            {
                PageKey = pageKey
            };

            SiteAwareEntityObjectMediator.SetSiteEntity(commandParameter);

            AdministrationAreaMediator.SetAreaFlag(commandParameter);

            command.ConfigureCommand(commandParameter);

            return FindOne(command);
        }

        /// <summary>
        /// Finds pages by page key
        /// </summary>
        /// <param name="pageKey">Page key of page to find</param>
        /// <returns>An object of <see cref="CmsPage" /> if page exists or null if does not</returns>
        /// <remarks>Can return different pages in admin area and front end. It depends on workflow.</remarks>
        public virtual IEnumerable<CmsPage> GetPagesByPageKey(string pageKey)
        {
            var command = DependencyServiceLocator.Current.GetInstance<CmsPageSelectAllByPageKeyCommand>();

            var commandParameter = new CmsPageSelectAllByPageKeyCommandParameter
            {
                PageKey = pageKey
            };

            SiteAwareEntityObjectMediator.SetSiteEntity(commandParameter);

            AdministrationAreaMediator.SetAreaFlag(commandParameter);

            command.ConfigureCommand(commandParameter);

            return Find(command);
        }

        /// <summary>
        /// Finds all pages within website
        /// </summary>
        /// <returns>Collection of pages</returns>
        public override IEnumerable<CmsPage> FindAll()
        {
            return InnerFindAll();
        }

        public virtual IEnumerable<CmsPage> GetPageRoutes()
        {
            var command = DependencyServiceLocator.Current.GetInstance<CmsPageTypeResolverCommand>();

            var commandParameter = new CmsPageTypeResolverCommandParameter();

            command.ConfigureCommand(commandParameter);

            return Find(command);
        }

        public virtual IEnumerable<CmsPage> GetParentsPages(int pageId, bool includeStartPage = false)
        {
            return GetFamilyPages(pageId, true, includeStartPage, null);
        }

        public virtual IEnumerable<CmsPage> GetChildPages(int pageId, bool includeStartPage = false, int? plusLevel = null)
        {
            return GetFamilyPages(pageId, false, includeStartPage, plusLevel);
        }

        public virtual CmsPage GetPageByUrl(string url)
        {
            Guard.ArgumentNotEmpty(url, "url");

            var command = DependencyServiceLocator.Current.GetInstance<CmsPageFrontEndCommand>();

            var commandParameter = new CmsPageFrontEndCommandParameter
            {
                PageUrl = url
            };

            SiteAwareEntityObjectMediator.SetSiteEntity(commandParameter);

            command.ConfigureCommand(commandParameter);

            return FindOne(command);
        }

        public virtual CmsPage PreviewPage(int pageId)
        {
            var command = DependencyServiceLocator.Current.GetInstance<CmsPagePreviewCommand>();

            var commandParameter = new CmsPagePreviewCommandParameter
            {
                Id = pageId
            };

            SiteAwareEntityObjectMediator.SetSiteEntity(commandParameter);

            command.ConfigureCommand(commandParameter);

            return FindOne(command);
        }

        public virtual void MovePage(int pageId, int moveToPageId)
        {
            var command = DependencyServiceLocator.Current.GetInstance<CmsPageMoveCommand>();

            var commandParameter = new CmsPageMoveCommandParameter
            {
                PageId = pageId,
                MoveToPageId = moveToPageId
            };

            command.ConfigureCommand(commandParameter);

            Execute(command);
        }

        public virtual void ReparentPage(int pageId, int newParentPageId)
        {
            var command = DependencyServiceLocator.Current.GetInstance<CmsPageReallocateCommand>();

            var commandParameter = new CmsPageMoveCommandParameter
            {
                PageId = pageId,
                MoveToPageId = newParentPageId
            };

            command.ConfigureCommand(commandParameter);

            Execute(command);
        }

        public override IEnumerable<CmsPage> FindAllRevisions(CmsPage currentPage)
        {
            Guard.ArgumentNotNull(currentPage, "currentPage");

            var command = DependencyServiceLocator.Current.GetInstance<CmsEntityGetAllRevisionsCommand<CmsPage>>();

            var commandParameter = new CmsEntityWorkflowRevisionsCommandParameter
            {
                Id = currentPage.Id
            };

            SiteAwareEntityObjectMediator.SetSiteEntity(commandParameter);

            command.ConfigureCommand(commandParameter);

            return Find(command);
        }

        public override IEnumerable<CmsPage> FindAllDraftRevisions(SiteAwareCategory siteAwareCategory = SiteAwareCategory.Site)
        {
            var command = DependencyServiceLocator.Current.GetInstance<CmsEntityGetDraftRevisionsCommand<CmsPage>>();

            var commandParameter = new CmsEntityWorkflowRevisionsCommandParameter();

            SiteAwareEntityObjectMediator.SetSiteEntityByCategory(commandParameter, siteAwareCategory);

            command.ConfigureCommand(commandParameter);

            return Find(command);
        }

        public override void ProcessWorkflow(CmsPage entity, string notes, int transitionId = 0, int? assignedUserId = new int?())
        {
            base.ProcessWorkflow(entity, notes, transitionId, assignedUserId);
        }

        public override IEnumerable<CmsPage> FindAllActiveRevisions(SiteAwareCategory siteAwareCategory = SiteAwareCategory.Site)
        {
            var command = DependencyServiceLocator.Current.GetInstance<CmsEntityGetActiveRevisionsCommand<CmsPage>>();

            var commandParameter = new CmsEntityWorkflowRevisionsCommandParameter();

            SiteAwareEntityObjectMediator.SetSiteEntityByCategory(commandParameter, siteAwareCategory);

            command.ConfigureCommand(commandParameter);

            return Find(command);
        }

        public override IEnumerable<CmsPage> FindAllApprovedRevisions(SiteAwareCategory siteAwareCategory = SiteAwareCategory.Site)
        {
            var command = DependencyServiceLocator.Current.GetInstance<CmsEntityGetApprovedRevisionsCommand<CmsPage>>();

            var commandParameter = new CmsEntityWorkflowRevisionsCommandParameter();

            SiteAwareEntityObjectMediator.SetSiteEntityByCategory(commandParameter, siteAwareCategory);

            command.ConfigureCommand(commandParameter);

            return Find(command);
        }

        public override CmsPage GetCurrentRevision(int entityId)
        {
            return FindOne(entityId);
        }

        public override void Save(CmsPage entityObject)
        {
            Validate(entityObject, ValidationActionType.Update);

            var command = DependencyServiceLocator.Current.GetInstance<CmsPageSaveCommand>();

            TrackingEntityObjectMediator.TrackEntity(entityObject);

            var commandParameter = new CmsPageSaveCommandParameter() { Page = entityObject };

            command.ConfigureCommand(commandParameter);

            Execute(command);
        }

        public virtual CmsPage Copy(int entityId)
        {
            var command = DependencyServiceLocator.Current.GetInstance<CmsPageCopyCommand>();

            var commandParameter = new CmsPageCopyCommandParameter
            {
                PageId = entityId,
            };

            TrackingEntityObjectMediator.TrackEntity(commandParameter);

            command.ConfigureCommand(commandParameter);

            return FindOne(command, true);
        }

        protected virtual IEnumerable<CmsPage> InnerFindAll(int? pageLevel = null)
        {
            var command = DependencyServiceLocator.Current.GetInstance<CmsPageSelectAllCommand>();

            var commandParameter = new CmsPageSelectAllCommandParameter();

            if (pageLevel.HasValue)
            {
                commandParameter.PageLevel = pageLevel;
            }

            SiteAwareEntityObjectMediator.SetSiteEntity(commandParameter);

            AdministrationAreaMediator.SetAreaFlag(commandParameter);

            command.ConfigureCommand(commandParameter);

            return Find(command);
        }

        protected virtual IEnumerable<CmsPage> GetFamilyPages(int pageId, bool isSearchParents, bool includeStartPage, int? plusLevel)
        {
            var command = DependencyServiceLocator.Current.GetInstance<CmsPageSelectFamilyPagesCommand>();

            var commandParameter = new CmsPageSelectFamilyPagesCommandParameter
            {
                Id = pageId,
                IsSearchParents = isSearchParents,
                IncludeStartPage = includeStartPage,
                PlusLevel = plusLevel
            };

            SiteAwareEntityObjectMediator.SetSiteEntity(commandParameter);
            AdministrationAreaMediator.SetAreaFlag(commandParameter);

            command.ConfigureCommand(commandParameter);

            var data = Find(command).ToList();

            var result = (data != null) && data.Any() ?
                data.ToList() :
                new List<CmsPage>();

            int lenght = result.Count;
            CmsPage item = null;
            for (int i = 0; i < lenght; i++)
            {
                item = result[i];

                if (item.ParentPageId.HasValue)
                {
                    if ((i > 0) && (i < lenght - 1))
                    {
                        item.PreviousSibling = result[i - 1];
                        item.NextSibling = result[i + 1];

                        //if our prev page is root then we have not prev
                        if (!item.PreviousSibling.ParentPageId.HasValue)
                        {
                            item.PreviousSibling = null;
                        }
                    }
                    else if ((i == 0) && (i < lenght - 1))
                    {
                        item.NextSibling = result[i + 1];
                    }
                    else if ((i == lenght - 1) && (lenght > 2))
                    {
                        item.PreviousSibling = result[i - 1];
                    }
                }
            }

            return result;
        }

        public virtual CmsPageCounts GetCountsForDifferentStatusPages(int pageId)
        {
            var command = DependencyServiceLocator.Current.GetInstance<CmsPageGetCountsCommand>();
            var commandParameter = new CmsPageGetCountsCommandParameter
            {
                Id = pageId
            };

            SiteAwareEntityObjectMediator.SetSiteEntity(commandParameter);
            command.ConfigureCommand(commandParameter);

            return new BusinessServiceFacade<CmsPageCounts, int>().FindOne(command);
        }

        public virtual IEnumerable<CmsPage> FindAllByTemplateId(int templateId)
        {
            var command = DependencyServiceLocator.Current.GetInstance<CmsPageSelectAllByTemplateIdDataCommand>();
            var commandParameter = new CmsPageSelectAllByTemplateIdCommandParameter()
            {
                TemplateId = templateId
            };

            command.ConfigureCommand(commandParameter);
            return new BusinessServiceFacade<CmsPage, int>().Find(command);
        }
    }
}